/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 resurrection_spell.cpp

	$Header: /heroes4/resurrection_spell.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "resurrection_spell.h"

#include "adaptor_handler.h"
#include "battlefield.h"
#include "battlefield_window.h"
#include "bitmap_group_cache.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_ai.h"
#include "combat_ai_spell_single_action.h"
#include "combat_creature.h"
#include "combat_spell_registration.h"
#include "creature.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "external_string.h"
#include "format_string.h"
#include "game_time.h"
#include "hero.h"
#include "missile_type.h"
#include "platform.h"
#include "play_combat_animation.h"
#include "random.h"
#include "replace_keywords.h"
#include "scroll_menu.h"
#include "simple_dialog.h"
#include "spell_properties.h"
#include "summoning_spell.h"

static t_combat_spell_registration<t_resurrection_spell>
       k_resurrection_register( k_spell_resurrection );

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
t_resurrection_spell::t_resurrection_spell( t_battlefield& battlefield, t_spell spell )
                    : t_combat_spell_single_target( battlefield, spell)
{
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// destructor
// -----------------------------------------------------------------------
t_resurrection_spell::~t_resurrection_spell()
{
	if (m_menu != 0)
		m_menu->close();
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// get effective power
// -----------------------------------------------------------------------
int t_resurrection_spell::get_basic_health_power() const
{
	return m_caster->get_spell_power( m_spell );
}

int t_resurrection_spell::get_health_power( t_combat_creature const& target ) const
{
	int result = get_basic_health_power();
	int damage = target.get_wounds() 
		         + target.get_salvageable_bodies() * target.get_hit_points();

	if (result > damage)
		result = damage;
	return result;
}


// -----------------------------------------------------------------------
// handle the resurrection spell
// get possible targets in a cell
// -----------------------------------------------------------------------
t_combat_creature_list t_resurrection_spell::get_targets( t_screen_point const& point ) const
{
	t_combat_creature_list result;
	bool                   defender = get_caster()->belongs_to_defender();
	t_battlefield&		   battlefield = m_battlefield;
	
	result = battlefield.get_resurrection_targets( point, defender, m_spell );
	if (result.empty())
	{
		t_combat_creature* creature = battlefield.creature_hit_test( point );

		if (creature != 0 && can_cast_on( creature, 0 ))
			result.push_back( creature );
	}
	return result;
}

double t_resurrection_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
double t_resurrection_spell::ai_weight( t_combat_creature const& target ) const
{
	int healed = get_health_power( target );

	double result = target.get_ai_value_per_hit() * healed;

	// despite the function name, casting resurrection is particularly
	// worthwhile when other spells are not.
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		result *= 10.0;
	else
		result *= get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
bool t_resurrection_spell::can_cast( t_screen_point const& point ) const
{
	t_map_point_2d         map_point;

	if (!m_battlefield.cell_hit_test( point, map_point ))
		return false;
	if (!m_battlefield.can_see_cell( *get_caster(), map_point ))
		return false;
	return !get_targets( point ).empty();
}


// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
void t_resurrection_spell::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	m_battlefield.start_animation( *target, m_spell, message );
	if (target->get_number() == 0)
		target->show_resurrection( message );
	target->resurrect( get_health_power( *target ) );
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
static std::string get_name_text( t_combat_creature* creature, int number )
{
	if (creature->get_hero() == 0)
		return format_string( "%i ", number ) + creature->get_name( false, number );
	return creature->get_name();
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
static t_external_string const k_text_resurrect_creatures( "resurrect.spells" );
static t_external_string const k_text_heal_creatures( "heal.spells" );

std::string t_resurrection_spell::get_text( t_combat_creature* creature )
{
	int amount = get_health_power( *creature );

	if (amount <= creature->get_wounds())
		return replace_keywords( k_text_heal_creatures, "%creatures", creature->get_name());

	int         number;
	std::string help_text;

	number = (amount - creature->get_wounds()) / creature->get_hit_points() + 1;

	help_text = get_name_text( creature, number );
	return replace_keywords( k_text_resurrect_creatures, "%creatures", help_text );
}


// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------

t_mouse_window* t_resurrection_spell::mouse_move( t_screen_point const& point,
										          std::string& help_text )
{
	t_map_point_2d         map_point;
	t_combat_creature_list creatures;
	t_mouse_window*        result;
	t_combat_creature*     caster = get_caster();
	t_combat_creature*     target = 0;
	t_battlefield&		   battlefield = m_battlefield;
	
	result = m_battlefield.get_blocked_cursor();
	if (!battlefield.cell_hit_test( point, map_point ))
		return result;

	creatures = get_targets( point );
	if (creatures.empty())
	{
		t_battlefield_cell& cell = battlefield.get_cell( map_point );
		bool                defender = caster->belongs_to_defender();

		battlefield.select_target( 0 );
		creatures = battlefield.get_creatures_with_casualties( point, true, true );
		if (creatures.empty())
			target = battlefield.creature_hit_test( point );
		else
			target = creatures.front().get();
		if (target == 0)
			return result;
		result = m_battlefield.get_spell_cannot_cast_cursor();
		can_cast_on( target, &help_text);
		return result;
	}
	target = creatures.front().get();
	m_battlefield.select_target( target );
	help_text = get_text( target );
	return get_spell_cursor();
}

static void eliminate_duplicates( t_combat_creature_list& list )
{
	t_combat_creature_list::iterator	index;
	t_combat_creature_list::iterator	next;
	t_combat_creature_list::iterator	other_index;
	t_combat_creature*					creature;
	t_combat_creature*					other_creature;
	t_creature_type						creature_type;
	
	next = list.begin();
	while (next != list.end())
	{
		index = next;
		++next;
		creature = *index;
		creature_type = creature->get_creature_type();
		if (creature_type == k_creature_none)
			continue;
		other_index = index;
		++other_index;
		for (; other_index != list.end(); ++other_index)
		{
			other_creature = *other_index;
			if (other_creature->get_creature_type() != creature_type)
				continue;
			if (other_creature->get_number() < creature->get_number())
				std::swap( *index, *other_index );
			break;
		}
		if (other_index == list.end())
			continue;
		list.erase( index );
	}
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
bool t_resurrection_spell::left_click( t_screen_point const& point )
{
	t_map_point_2d         map_point;
	t_combat_creature_list creatures;
	bool                   defender = get_caster()->belongs_to_defender();
	
	creatures = get_targets( point );
	if (creatures.empty())
		return false;

	eliminate_duplicates( creatures );
	if (creatures.size() == 1)
	{
		resurrect( creatures.front().get() );
		return true;
	}
	
	t_combat_creature_list::iterator index;
	t_combat_creature_ptr            creature;
	t_handler                        handler;
	t_window*                        window = t_window::get_modal_window();

	m_menu = new t_scroll_menu( window );
	for (index = creatures.begin(); index != creatures.end(); index++)
	{
		creature = *index;
		handler = add_argument( bound_handler( *this, &t_resurrection_spell::resurrect ),
			                    creature );
		m_menu->add_item( get_text( creature ), handler );
	}
	m_menu->open( get_mouse_position( window ) );
	m_battlefield.get_window()->set_help_balloon_text( "" );
	return true;
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// -----------------------------------------------------------------------
void t_resurrection_spell::resurrect( t_combat_creature_ptr creature )
{
	m_spell_target = creature;
	if (!m_is_artifact)
		get_caster()->set_default_spell( m_spell );	
	m_battlefield.begin_spell( creature );
}

static t_combat_spell_registration<t_rebirth_spell>
       k_rebirth_register( k_spell_rebirth );

// -----------------------------------------------------------------------
// handler for the Rebirth spell
// -----------------------------------------------------------------------
t_rebirth_spell::t_rebirth_spell( t_battlefield& battlefield, t_spell spell )
               : t_resurrection_spell( battlefield, spell )
{
}

// -----------------------------------------------------------------------
// handler for the Rebirth spell
// -----------------------------------------------------------------------
bool t_rebirth_spell::begin_casting()
{
	std::string help_text;

	m_spell_target = get_caster();

	if (!::can_cast( *get_caster(), m_spell, *m_spell_target, &help_text ))
	{
		ok_dialog( help_text, true );
		return false;
	}
	m_battlefield.begin_action( false );
	execute( *get_caster() );
	return true;
}

// --------------------------------------------------------------------
// Generate the list of casting actions the given caster could take
// for this spell
// --------------------------------------------------------------------
t_combat_ai_action_list t_rebirth_spell::generate_combat_ai_action_list(t_combat_ai &owner)
{
	t_combat_ai_action_list result;

	t_combat_creature &actor = owner.get_actor();

	if (::can_cast(actor, m_spell, actor, 0))
	{
		result.push_back(new t_combat_ai_spell_single_action( m_battlefield, this, &actor));
	}

	return result;
}


// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
namespace
{
	class t_animate_dead : public t_resurrection_spell
	{
	public:
		t_animate_dead( t_battlefield& battlefield, t_spell spell );

		virtual double					ai_weight( t_combat_creature const& target ) const;
		virtual void					cast_and_mirror( t_combat_creature_ptr target, 
			                                             int power );
		virtual double					get_cancel_weight( t_combat_creature const& target ) const;
		virtual	t_creature_type			get_creature_type( t_combat_creature const& target ) const;
		virtual t_combat_creature_list	get_targets( t_screen_point const& point ) const;
	protected:
		virtual t_combat_creature_ptr	create_creature( t_combat_creature_ptr creature,
													     double scale, bool defender );
		virtual int						get_health_power( t_combat_creature const& target ) const;
		int								get_bodies_affected( t_combat_creature* creature ) const;
		virtual std::string				get_text( t_combat_creature* creature );
		virtual void					resurrect( t_combat_creature_ptr creature );
	};
};

static t_combat_spell_registration<t_animate_dead>
       k_animate_dead_register( k_spell_animate_dead );
static t_combat_spell_registration<t_animate_dead>
       k_death_call_register( k_spell_death_call );

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
t_animate_dead::t_animate_dead( t_battlefield& battlefield, t_spell spell )
              : t_resurrection_spell( battlefield, spell )
{
}

// -----------------------------------------------------------------------
// value of animate dead
// -----------------------------------------------------------------------
double t_animate_dead::ai_weight( t_combat_creature const& target ) const
{
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_battle_length( m_battlefield ) < 2)
		return 0.0;
	return t_resurrection_spell::ai_weight( target );
}


t_creature_type t_animate_dead::get_creature_type( t_combat_creature const& target ) const
{
	return target.get_creature_type();
}

int t_animate_dead::get_health_power( t_combat_creature const& target ) const
{
	int result = get_basic_health_power();
	int limit = target.get_salvageable_bodies() * target.get_hit_points();

	if (result > limit)
		result = limit;
	return result;
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
void t_animate_dead::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_battlefield&	battlefield	= m_battlefield;
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	battlefield.start_animation( *target, m_spell, message );

	int         number = get_bodies_affected( target );
	t_direction direction;
	int			health = get_health_power( *target );

	// find a suitably large open spot
	t_combat_creature&		caster		 = *get_caster();
	double					scale		 = battlefield.get_model_scale();
	bool					defender	 = caster.get_controller();
	t_combat_creature_ptr	creature;

	creature = battlefield.find_summoned_creature( m_spell, get_creature_type( *target ),
													caster.get_controller() );
	if (creature != 0)
	{
		creature->add_creatures( health );
		if (target->get_number() == 0 && target->get_salvageable_bodies() <= number)
			target->fade_out( message );
	}
	else
	{
		creature = create_creature( target, scale, defender );
		creature->set_number( 0 );

		if (!place_summoned_creature( caster, creature ))
			return;

		// check if there are no bodies left
		if (target->get_number() == 0 && target->get_salvageable_bodies() <= number)
		{
			// if the creature is not in the same cell...
			if (target->get_cell_position() != creature->get_cell_position()
				|| target->get_creature_type() != creature->get_creature_type())
				target->fade_out( message );
			else
			{
				// if there are no bodies left, hide the target and replace it with the new creature
				target->set_alpha( m_battlefield, 0 );
				// make the creature face the same direction as the target
				direction = target->get_current_direction();
				creature->set_current_direction( direction );
				creature->set_alpha( m_battlefield, 15 );
			}
			target->set_normal_alpha( 0 );
		}
		// play death animation in reverse
		creature->set_current_action( k_combat_actor_action_die );
		creature->set_spell( m_spell, message );
		battlefield.place_object( creature, t_map_point_2d( creature->get_position() ));
		creature->show_resurrection( message );
	}

	// remove bodies from target
	target->remove_bodies( number );
	creature->resurrect( health );
	play_sound();
	// if creature is invisible, fade it in.
	if (creature->get_alpha() < 15)
		creature->fade_in( message );
	battlefield.set_spell( 0 );
}

double t_animate_dead::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_ai_value( target.get_total_hits() );

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
t_combat_creature_list t_animate_dead::get_targets( t_screen_point const& point ) const
{
	t_combat_creature_list			 result;
	t_combat_creature_list::iterator last;
	t_combat_creature_list::iterator index;
	std::string               help;

	result = m_battlefield.get_creatures_with_casualties( point, true, true );
	index = result.begin();
	while (index != result.end())
	{
		last = index;
		index++;
		if (!::can_cast( *m_caster, m_spell, **last ))
			result.erase( last );
	}
	return result;
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
int	t_animate_dead::get_bodies_affected( t_combat_creature* creature ) const
{
	int amount = get_basic_health_power();
	int number;
	int limit;

	number = (amount + creature->get_hit_points() - 1) / creature->get_hit_points();
	limit = creature->get_salvageable_bodies();
	if (number > limit)
		number = limit;
	return number;
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
static t_external_string const k_text_animate_dead( "animate_dead.spells" );

std::string t_animate_dead::get_text( t_combat_creature* creature )
{
	int         number = get_bodies_affected( creature );
	std::string help_text;

	help_text = get_name_text( creature, number );
	return replace_keywords( k_text_animate_dead, "%creatures", help_text );
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// create the newly-animated creature
// -----------------------------------------------------------------------
t_combat_creature_ptr t_animate_dead::create_creature( t_combat_creature_ptr creature,
									                   double scale, bool defender )
{
	// get hero or a new set of creatures
	t_creature_stack*  creature_stack;

	if (creature->get_hero() == 0)
		creature_stack = new t_creature( creature->get_creature_type(), 
		                                 get_bodies_affected( creature ) );
	else
		creature_stack = creature->get_hero();

	t_creature_array& army = m_battlefield.get_army( get_caster()->get_controller() );
	t_player const* owner = m_battlefield.get_player( defender );
	return new t_combat_creature( m_battlefield, creature_stack, -1, 0, 
		                         scale, defender, owner );
}

// -----------------------------------------------------------------------
// handler for the Animate Dead spells
// -----------------------------------------------------------------------
void t_animate_dead::resurrect( t_combat_creature_ptr creature )
{
	// find a suitably large open spot
	t_combat_creature&		caster		 = *get_caster();
	double					scale		 = m_battlefield.get_model_scale();
	bool					defender		 = caster.get_controller();
	t_combat_creature_ptr	new_creature;

	m_spell_target = creature;
	new_creature = create_creature( creature, scale, defender );
	new_creature->set_number( 0 );

	// find closest point to place this creature
	if (!place_summoned_creature( caster, new_creature ))
		return;

	if (!m_is_artifact)
		get_caster()->set_default_spell( m_spell );	
	m_battlefield.begin_spell( creature );
}

// -----------------------------------------------------------------------
// handler for the Raise Undead
// -----------------------------------------------------------------------
namespace
{
	class t_raise_undead : public t_animate_dead
	{
	public:
		t_raise_undead( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual t_creature_type get_creature_type( t_combat_creature const& target ) const;
	protected:
		virtual t_combat_creature_ptr create_creature( t_combat_creature_ptr creature,
									                   double scale, bool defender );
		virtual int			get_basic_health_power() const;
		virtual int			get_health_power( t_combat_creature const& target ) const;	
		int					get_undead_number( t_combat_creature const* creature ) const;
		virtual std::string	get_text( t_combat_creature* creature );
	};
};

inline t_raise_undead::t_raise_undead( t_battlefield& battlefield, t_spell spell )
              : t_animate_dead( battlefield, spell )
{
}

static t_combat_spell_registration<t_raise_undead>
       k_raise_ghost_registration( k_spell_raise_ghost );
static t_combat_spell_registration<t_raise_undead>
       k_raise_skeletons_registration( k_spell_raise_skeletons );
static t_combat_spell_registration<t_raise_undead>
       k_raise_vampires_registration( k_spell_raise_vampires );

t_creature_type t_raise_undead::get_creature_type( t_combat_creature const& target ) const
{
	return get_summoned_creature( m_spell );
}

// -----------------------------------------------------------------------
// handler for the Raise Undead
// -----------------------------------------------------------------------
t_combat_creature_ptr t_raise_undead::create_creature( t_combat_creature_ptr creature,
									                   double scale, bool defender )
{
	// create undead
	t_creature_type    type = get_summoned_creature( m_spell );
	t_creature_stack*  creature_stack;

	creature_stack = new t_creature( type, get_undead_number( creature ));
	t_creature_array& army = m_battlefield.get_army( get_caster()->get_controller() );
	t_player const* owner = m_battlefield.get_player( defender );
	return new t_combat_creature( m_battlefield, creature_stack, -1, 0, 
		                          scale, defender, owner );
}

double t_raise_undead::ai_weight( t_combat_creature const& target ) const
{
	t_creature_type    type = get_summoned_creature( m_spell );

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_battle_length( m_battlefield ) < 2)
		return 0.0;
	return get_summoning_value( *m_caster, target, type, get_undead_number( &target ));
}

// -----------------------------------------------------------------------
// handler for the Raise Undead
// -----------------------------------------------------------------------
int	t_raise_undead::get_basic_health_power() const
{
	t_creature_type    type = get_summoned_creature( m_spell );
	int				   hits = get_traits( type ).hit_points;

	return m_caster->get_spell_power( m_spell ) * hits;
}

int t_raise_undead::get_health_power( t_combat_creature const& target ) const
{
	t_creature_type	type = get_summoned_creature( m_spell );
	int				hits = get_traits( type ).hit_points;

	return get_undead_number( &target ) * hits;
}

// -----------------------------------------------------------------------
// handler for the Raise Undead
// -----------------------------------------------------------------------
int	t_raise_undead::get_undead_number( t_combat_creature const* creature ) const
{
	int				   amount = m_caster->get_spell_power( m_spell );
	t_creature_type    type = get_summoned_creature( m_spell );
	int				   hits = get_traits( type ).hit_points;
	int				   limit;
	int				   source_hits;

	limit = creature->get_salvageable_bodies();
	source_hits = creature->get_hit_points() * limit;
	if (amount > limit)
		amount = limit;
	limit = (source_hits + hits - 1) / hits;
	if (amount > limit)
		amount = limit;
	return amount;
}

// -----------------------------------------------------------------------
// handler for the Raise Undead
// -----------------------------------------------------------------------
static t_external_string const k_text_raise( "raise_undead.spells" );

std::string t_raise_undead::get_text( t_combat_creature* creature )
{
	t_creature_type type = get_summoned_creature( m_spell );
	int				number = get_undead_number( creature );
	std::string		help_text;

	help_text = format_string( "%i ", number ) + get_creature_name( type, number, false );
	return replace_keywords( k_text_raise, 
		                     "%undead", help_text,
							 "%the_creatures", creature->get_name( true ));
}

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
namespace
{
	class t_acid : public t_animate_dead
	{
	public:
		t_acid( t_battlefield& battlefield, t_spell spell );

		virtual void					cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual double					get_cancel_weight( t_combat_creature const& target ) const;
		virtual t_combat_creature_list	get_targets( t_screen_point const& point ) const;
		void							potion_impact( t_combat_creature_ptr	caster, 
													   t_map_point_2d			square, 
													   bool						is_mirror );
	protected:
		virtual std::string get_text( t_combat_creature* creature );
		virtual void		resurrect( t_combat_creature_ptr creature );
	};
};

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
inline t_acid::t_acid( t_battlefield& battlefield, t_spell spell )
             : t_animate_dead( battlefield, spell)
{
}

static t_combat_spell_registration<t_acid>
       k_acid_register( k_spell_acid );

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
void t_acid::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_map_point_3d    target_center = target->get_body_center();
	t_missile_handler handler;
	t_map_point_3d    source_position;
	t_combat_action_message message( *m_caster, get_action_text( false ) ) ;

	source_position = m_caster->get_spell_origin();
	handler = add_3rd_argument( bound_handler( *this, &t_acid::potion_impact ), false );
	m_battlefield.launch_missile( k_missile_potion, *m_caster, source_position, 
		                           target_center, handler, message );
	m_battlefield.set_spell( this ); // keep a reference to this spell in battlefield
}

t_combat_creature_list t_acid::get_targets( t_screen_point const& point ) const
{
	t_map_point_2d							map_point;
	bool									map_point_valid;
	t_combat_creature_list					result;
	t_combat_creature_list::iterator end = m_battlefield.creatures_end();
	t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
	t_uint32								current_time = get_time();
	t_combat_creature*						creature;

	map_point_valid = m_battlefield.cell_hit_test( point, map_point );
	for ( ; index != end; ++index )
	{
		creature = index->get();
		if (creature->get_alpha() == 0)
			continue;
		if (creature->get_number() > 0)
		{
			if (!can_affect( *m_caster, m_spell, *creature ))
				continue;
		}
		else if (creature->get_salvageable_bodies() == 0)
			continue;

		if ( (!map_point_valid || !creature->in_footprint( map_point ))
			&& !m_battlefield.object_hit_test( *creature, current_time, point ) )
			continue;
		result.push_back( creature );
	}
	return result;
}

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
void t_acid::potion_impact( t_combat_creature_ptr caster, t_map_point_2d square, bool is_mirror )
{
	t_combat_action_message message( *caster, get_action_text( is_mirror ) ) ;

	m_battlefield.start_animation( *m_spell_target, m_spell, message );
	if (m_spell_target->get_number() == 0)
	{
		int bodies = m_spell_target->get_salvageable_bodies();

		m_spell_target->remove_bodies( bodies );
		m_spell_target->fade_out( message );
		return;
	}
	m_spell_target->set_spell( m_spell, message );
	m_spell_target->set_animation( k_combat_actor_action_flinch );
}

double t_acid::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
static t_external_string const k_text_destroy_bodies( "destroy.spells" );

std::string t_acid::get_text( t_combat_creature* creature )
{
	std::string help_text;

	if (creature->get_number() == 0)
	{
		int bodies = creature->get_salvageable_bodies();

		help_text = get_name_text( creature, bodies );
		return replace_keywords( k_text_destroy_bodies, "%creatures", help_text );
	}
	::can_cast( *m_caster, m_spell, *creature, &help_text);
	return help_text;
}

// -----------------------------------------------------------------------
// handler for Acid
// -----------------------------------------------------------------------
void t_acid::resurrect( t_combat_creature_ptr creature )
{
	m_spell_target = creature;
	m_battlefield.begin_spell( creature );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
namespace
{
	class t_sacrifice : public t_resurrection_spell
	{
	public:
		t_sacrifice( t_battlefield& battlefield, t_spell spell );

		virtual bool				    can_cast( t_screen_point const& point ) const;
		virtual bool				    can_cast_on( t_combat_creature* creature,
			                                         std::string*		help_text ) const;
		virtual void				    cast_and_mirror( t_combat_creature_ptr target, 
														 int power );
		virtual t_combat_ai_action_list generate_combat_ai_action_list(t_combat_ai &owner);
		virtual t_mouse_window*			get_spell_cursor() const;
		virtual t_combat_creature_list  get_targets( t_screen_point const& point ) const;
		virtual int					    get_value( t_combat_creature* target ) const;
		virtual t_mouse_window*		    mouse_move( t_screen_point const& point,
										 		    std::string& help_text );
		virtual bool				    left_click( t_screen_point const& point );
	protected:
		void                do_resurrection( t_window*, t_combat_action_message message );
		virtual int			get_basic_health_power() const;
		virtual void		resurrect( t_combat_creature_ptr creature );

		t_combat_creature_ptr	m_client;
		int						m_power;
		t_bitmap_cursor			m_sacrifice_client_cursor;
		t_bitmap_cursor			m_sacrifice_cursor;
		bool					m_selecting_source;
	};
};

static t_combat_spell_registration<t_sacrifice>
       k_sacrifice_register( k_spell_sacrifice );
static t_bitmap_group_cache const k_sacrifice_cursor( "cursor.combat.sacrifice" );
static t_bitmap_group_cache const k_sacrifice_client_cursor( "cursor.combat.sacrifice_client" );
// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
t_sacrifice::t_sacrifice( t_battlefield& battlefield, t_spell spell )
           : t_resurrection_spell( battlefield, spell )
{
	m_selecting_source = true;
	m_sacrifice_cursor.create( k_sacrifice_cursor.get() );
	m_sacrifice_client_cursor.create( k_sacrifice_client_cursor.get() );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
bool t_sacrifice::can_cast( t_screen_point const& point ) const
{
	if (m_selecting_source)
		t_combat_spell_single_target::can_cast( point );
	return t_resurrection_spell::can_cast( point );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
int t_sacrifice::get_basic_health_power() const
{
	return m_power;
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
static t_external_string const k_text_is_being_sacrificed( "sacrifice.is.spells" );
static t_external_string const k_text_are_being_sacrificed( "sacrifice.are.spells" );

bool t_sacrifice::can_cast_on( t_combat_creature* creature,
							   std::string* help_text ) const
{
	if (m_selecting_source)
		return ::can_cast( *m_caster, m_spell, *creature, help_text );
	if (!::can_cast( *m_caster, k_spell_effect_sacrifice_client, *creature, help_text ))
		return false;
	if (creature == m_client)
	{
		if (help_text != 0)
		{
			if (creature->get_number() == 1)
				*help_text = replace_keywords( k_text_is_being_sacrificed, "%the_creature",
			                                   creature->get_name( true ));
			else
				*help_text = replace_keywords( k_text_are_being_sacrificed, "%the_creatures",
			                                   creature->get_name( true ));
		}
		return false;
	}
	return true;
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
void t_sacrifice::do_resurrection( t_window*, t_combat_action_message message )
{
	m_battlefield.start_animation( *m_client, k_spell_resurrection, message );
	if (m_client->get_number() == 0)
		m_client->show_resurrection( message );
	m_client->resurrect( m_power );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
void t_sacrifice::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_window_handler handler;
	int				 number = m_spell_target->get_number();
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	assert( m_spell_target != m_client );
	handler = add_2nd_argument( bound_handler( *this, &t_sacrifice::do_resurrection ), message );
	m_power = target->get_total_hits() * 2;
	m_battlefield.start_animation( *m_spell_target, k_spell_sacrifice, handler, message );
	// remove guardian angel - these guys are going to DIE.
	m_spell_target->clear_spell( k_spell_guardian_angel, message ); 
	m_spell_target->kill_permanently( number, message );
	m_battlefield.set_spell( this ); // reset spell so we don't get deleted.
}

// -----------------------------------------------------------------------
// handle the resurrection spell
// get possible targets in a cell
// -----------------------------------------------------------------------
t_combat_creature_list t_sacrifice::get_targets( t_screen_point const& point ) const
{
	t_combat_creature_list			 result;
	
	result = t_resurrection_spell::get_targets( point );
	result.remove( m_spell_target );
	return result;
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
int	t_sacrifice::get_value( t_combat_creature* target ) const
{
	return 0;
}

// --------------------------------------------------------------------
// Generate the list of casting actions the given caster could take
// for this spell
// --------------------------------------------------------------------
t_combat_ai_action_list t_sacrifice::generate_combat_ai_action_list(t_combat_ai &owner)
{
	// Not implemented
	return t_combat_ai_action_list();
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
t_mouse_window* t_sacrifice::get_spell_cursor() const
{
	if (m_selecting_source)
		return m_sacrifice_cursor.cursor;
	return m_sacrifice_client_cursor.cursor;
}

t_mouse_window*	t_sacrifice::mouse_move( t_screen_point const& point, 
										 std::string& help_text )
{
	if (m_selecting_source)
		return t_combat_spell_single_target::mouse_move( point, help_text );
	return t_resurrection_spell::mouse_move( point, help_text );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
static t_external_string const k_text_choose_sacrifice_client( "sacrifice_client.spells" );
bool t_sacrifice::left_click( t_screen_point const& point )
{
	if (m_selecting_source)
	{
		t_combat_creature* creature = m_battlefield.creature_hit_test( point );

		if (creature == 0)
			return false;

		if (!::can_cast( *m_caster, m_spell, *creature ))
			return false;

		m_spell_target = creature;
		m_battlefield.add_prompt( *creature, k_text_choose_sacrifice_client );
		m_spell = k_spell_effect_sacrifice_client;
		m_selecting_source = false;
		m_battlefield.get_window()->update_cursor();
		return true;
	}
	return t_resurrection_spell::left_click( point );
}

// -----------------------------------------------------------------------
// handler for Sacrifice
// -----------------------------------------------------------------------
void t_sacrifice::resurrect( t_combat_creature_ptr creature )
{
	m_client = creature;
	m_battlefield.begin_spell( m_spell_target );
}
